import com.fasterxml.jackson.databind.ObjectMapper;
import com.icss.struct.URLMapping;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class Server {

    public static void main(String[] args) throws IOException, InterruptedException {

        ServerSocketChannel ssc = ServerSocketChannel.open();//监听新进来的 TCP连接的通道，打开 ServerSocketChannel

        ssc.socket().bind(new InetSocketAddress(9090));//绑定8080端口

        ssc.configureBlocking(false);//设置非阻塞模式

        Selector selector = Selector.open();//创建选择器

        SelectionKey selectionKey = ssc.register(selector, SelectionKey.OP_ACCEPT);//给选择器注册通道
        Executor pool = Executors.newCachedThreadPool();
        //selectionKey：代表了注册到该 Selector 的通道

        while (true) { //监听新进来的连接
            int select = selector.select(2000);
            if (select == 0) { //如果选择的通道为0，最长会阻塞 timeout毫秒
                continue;
            }

            System.out.println("开始处理请求.....");

            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();//迭代器

            while (keyIter.hasNext()) {

                new HttpHandler(keyIter).run();
                selector.wakeup();
            }
            selector.selectedKeys().clear();
        }
    }
}

class HttpHandler implements Runnable {

    private Iterator<SelectionKey> keyIter;
    private int buffersize = 1024;//设置缓冲区大小

    private String localCharSet = "UTF-8"; //设置编码格式

    private SelectionKey key;//注册号的通道

    public HttpHandler(Iterator<SelectionKey> keyIter) {//把选择键构造进去
        SelectionKey key = keyIter.next();
        this.keyIter = keyIter;
        this.key = key;
    }

    public void handleAccept() throws IOException {

        SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();

        socketChannel.configureBlocking(false);//设置非阻塞模式

        socketChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(buffersize));
        //buffer分配一个缓冲区 大小为1024

    }

    public void handleRead() {

        SocketChannel sc = (SocketChannel) key.channel();// SocketChannel 是一个连接到 TCP 网络套接字的通道
//        selectionKey.cancel();
//        selector.wakeup();
        ByteBuffer buffer = (ByteBuffer) key.attachment();//从 SocketChannel读取到的数据将会放到这个 buffer中

        buffer.clear();

        try {
            if ((sc.read(buffer)) != -1) {

                buffer.flip();//flip方法将Buffer从写模式切换到读模式

                // 读取请求
                String receive = Charset.forName(localCharSet).newDecoder().decode(buffer).toString();
                //将此 charset 中的字节解码成 Unicode 字符
                String[] requestMessage = receive.split("\r\n");//接受请求的信息

                for (String message : requestMessage) {
                    if (message.isEmpty()) {//如果是空行说明信息已经结束了
                        break;
                    }
                }
                //控制台打印
                // 第一行
                String[] firsetLine = requestMessage[0].split(" ");
///login.action  ?  username=tom1  &  password=123456
                // 获取请求
                String path = firsetLine[1].split("\\?")[0];
                try {
                    HashMap<String, String> paramsMap = new HashMap<>();
                    if ("POST".equalsIgnoreCase(firsetLine[0])) {
                        String jsonString = receive.substring(receive.indexOf("\r\n\r\n")).replace("\r\n", "");
                        ObjectMapper mapper = new ObjectMapper();
                        paramsMap = mapper.readValue(jsonString, HashMap.class);
                    } else if (firsetLine[1].indexOf("?") != -1){

                        String[] params = firsetLine[1].split("\\?")[1].split("&");

                        for (String item : params) {
                            paramsMap.put(item.split("=")[0], item.split("=")[1]);
                        }
                    }
                    if (URLMapping.PATH_MAPPING.get(path) == null) {
                        System.out.println(path);

                        InputStream in = Server.class.getResourceAsStream("/404.html");
                        StringBuilder sendStr404 = new StringBuilder();
                        sendStr404.append("HTTP/1.1 200 OK\r\n");
                        sendStr404.append("Connection: keep-alive\r\n");
                        sendStr404.append("Content-Type:text/html;charset=" + localCharSet + "\r\n");
                        sendStr404.append("\r\n");

                        BufferedReader fr = new BufferedReader(new InputStreamReader(in));
                        while (fr.ready()) {
                            sendStr404.append(fr.readLine());
                        }
                        ByteBuffer bf1 = ByteBuffer.allocate(2046);
                        bf1 = ByteBuffer.wrap(sendStr404.toString().getBytes(localCharSet));
                        sc.write(bf1);
                        sc.close();
                        return;
                    }
                    String classname = URLMapping.PATH_MAPPING.get(path).split("@")[0];
                    String mtd = URLMapping.PATH_MAPPING.get(path).split("@")[1];

                    /*
                    利用和反射
                       com.icss.struct.action.UserAction@login
                       调用 UserAction类的 login方法 传参数 paramsMap
                     */
                    // 反射：
                    Class clazz = Class.forName(classname);
                    Object action = clazz.getConstructor(null).newInstance(null);
                    // 调用类的方法
                    Object retobject = clazz.getMethod(mtd, HashMap.class).invoke(action, paramsMap);
                    // 返回值变成字符串
                    ObjectMapper objectMapper = new ObjectMapper();
                    String respstr = objectMapper.writeValueAsString(retobject);

                    // 编辑好返回的 数据类型 返回给客户端
                    ByteBuffer bf = ByteBuffer.allocate(1024);
                    StringBuilder sendStr = new StringBuilder();
                    sendStr.append("HTTP/1.1 200 OK\r\n");
                    sendStr.append("Connection: keep-alive\r\n");
                    sendStr.append("Content-Type:text/plain;charset=" + localCharSet + "\r\n");
                    sendStr.append("\r\n");
                    sendStr.append(respstr);
                    // 主体
//                        sendStr.append("<html><body><h1>hello http server socket</h1><h1>班号是啥??</h1></body></html>");
                    bf = ByteBuffer.wrap(sendStr.toString().getBytes(localCharSet));
                    sc.write(bf);
                    sc.close();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

                // 判断登录
//                System.out.println("----控制台输出：-------");
//                System.out.println("Method:t"+firsetLine[0]);
//                System.out.println("url是:\t"+firsetLine[1]);
//                System.out.println("Httpversion是:\t"+firsetLine[2]);
//                System.out.println("-----输出结束-------------");
            } else {
                sc.close();
            }
        } catch (IOException e) {

            e.printStackTrace();
        } finally {
            key.channel();

            keyIter.remove();
        }
    }

    @Override
    public void run() {

        try {
            if (key.isAcceptable()) {//接受
                handleAccept();
            }
            if (key.isReadable()) {//开始读
                handleRead();
            }

        } catch (Exception e) {

            e.printStackTrace();
        }
    }
}